﻿using System;
using System.Linq;
using System.Net;
using System.ServiceModel.Syndication;
using System.Xml;

namespace Wintellect.LiveFx.Cloudboard
{
  internal class NewVersionEventArgs : EventArgs
  {
    public VersionRecord VersionRecord { get; private set;}

    public NewVersionEventArgs(VersionRecord versionRecord)
    {
      this.VersionRecord = versionRecord;
    }
  }

  public class VersionRecord
  {
    public VersionRecord(String title, String description, String version, Uri uri)
    {
      this.Title = title;
      this.Description = description;
      this.Version = new Version(version);
      this.Uri = uri;
    }

    public String Title { get; private set;}
    public String Description { get; private set;}
    public Version Version { get; private set;}
    public Uri Uri { get; private set;}
  }

  internal class UpdateChecker
  {
    private System.Timers.Timer updateCheckTimer;

    public event EventHandler<NewVersionEventArgs> NewVersionAvailable;

    public UpdateChecker()
    {
      this.updateCheckTimer = new System.Timers.Timer(new TimeSpan(1, 0, 0, 0).TotalMilliseconds);
      this.updateCheckTimer.AutoReset = true;
      this.updateCheckTimer.Elapsed += delegate
      {
        this.CheckForUpdates();
      };
    }

    public void Start()
    {
      this.updateCheckTimer.Start();
      this.CheckForUpdates();
    }

    private void CheckForUpdates()
    {
      WebClient client = new WebClient();
      client.OpenReadCompleted += this.handleFeedResponse;
      client.OpenReadAsync(new Uri(Constants.CloudboardVersionFeedAddress));
    }

    private void handleFeedResponse(object sender, OpenReadCompletedEventArgs e)
    {
      if (e.Error == null)
      {
        try
        {
          using (XmlReader reader = XmlReader.Create(e.Result))
          {
            SyndicationFeed feed = SyndicationFeed.Load(reader);

            var versionEntries = from entry in feed.Items
                                 where (
                                 from ext in entry.ElementExtensions
                                 where ext.OuterNamespace == Constants.CloudboardNamespace && ext.OuterName == Constants.CloudboardVersionElementName
                                 select ext).FirstOrDefault() != null
                                 select new VersionRecord(
                                   entry.Title.Text,
                                   (entry.Content as TextSyndicationContent).Text,
                                   (from versionExtension in entry.ElementExtensions where versionExtension.OuterNamespace == Constants.CloudboardNamespace && versionExtension.OuterName == Constants.CloudboardVersionElementName select versionExtension).FirstOrDefault().GetReader().ReadElementString(),
                                   entry.Links[0].Uri);

            var maxVersion = (from entry in versionEntries where entry.Version == (from ev in versionEntries select ev.Version).Max() select entry).First();

            if (maxVersion.Version > System.Reflection.Assembly.GetEntryAssembly().GetName().Version)
            {
              this.OnNewVersion(maxVersion);
            }
          }
        }
        catch (System.Xml.XmlException xmlException)
        {
          EtwTrace.Trace(EtwTraceLevel.Warning, EtwTraceFlag.Network, "Unable to retrieve version feed.\n{0}", xmlException);
        }
        catch (System.FormatException formatException)
        {
          EtwTrace.Trace(EtwTraceLevel.Warning, EtwTraceFlag.Network, "Unable to retrieve version feed.\n{0}", formatException);
        }
      }
      else
      {
        EtwTrace.Trace(EtwTraceLevel.Warning, EtwTraceFlag.Network, "Unable to retrieve version feed.\n{0}", e.Error);
      }
    }

    private void OnNewVersion(VersionRecord record)
    {
      if (this.NewVersionAvailable != null)
      {
        App.Current.Dispatcher.Invoke((Action)delegate
        {
          this.NewVersionAvailable(this, new NewVersionEventArgs(record));
        });
      }
    }
  }
}
